package chain

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

type SmartContract struct {
	contractapi.Contract
}

type Medicine struct {
	MedicineCode string `json:"medicineCode"` // 药品防伪码
	MedicineName string `json:"medicineName"` // 药品名称
	MedicineId   string `json:"medicineId"`   // 药品Id
	ApprovalNo   string `json:"approvalNo"`   // 药品批准文号
	Unit         string `json:"unit"`         // 包装单位
	CreateDate   string `json:"createDate"`   // 上链时间
	Owner        string `json:"owner"`        // 所有者
	Receiver     string `json:"receiver"`     // 待接收交易者
	TractionType string `json:"tractionType"` // 最新交易类型
}

func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface, medicines []Medicine) error {
	for _, medicine := range medicines {
		medicineJSON, err := json.Marshal(medicine)
		if err != nil {
			return err
		}

		err = ctx.GetStub().PutState(medicine.MedicineId, medicineJSON)
		if err != nil {
			return err
		}
	}
	//log.Println(ctx.GetClientIdentity())
	return nil
}

// GetAllMedicine 获取所有资产
func (s *SmartContract) GetAllMedicine(ctx contractapi.TransactionContextInterface) ([]*Medicine, error) {
	resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
	if err != nil {
		return nil, err
	}
	defer func(resultsIterator shim.StateQueryIteratorInterface) {
		err := resultsIterator.Close()
		if err != nil {
			return
		}
	}(resultsIterator)

	var medicineInfos []*Medicine
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}

		var medicine Medicine
		err = json.Unmarshal(queryResponse.Value, &medicine)
		if err != nil {
			return nil, err
		}
		medicineInfos = append(medicineInfos, &medicine)
	}
	res, _ := json.MarshalIndent(medicineInfos, "", "\t")
	fmt.Println(res)
	return medicineInfos, nil
}

// ReadMedicine 获取资产状态
func (s *SmartContract) ReadMedicine(ctx contractapi.TransactionContextInterface, medicineId string) (*Medicine, error) {
	medicineJSON, err := ctx.GetStub().GetState(medicineId)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %v", err)
	}
	if medicineJSON == nil {
		return nil, fmt.Errorf("the medicine %s does not exist", medicineId)
	}

	var medicine Medicine
	err = json.Unmarshal(medicineJSON, &medicine)
	if err != nil {
		return nil, err
	}
	res, _ := json.MarshalIndent(medicine, "", "\t")
	fmt.Println(res)
	return &medicine, nil
}

func (s *SmartContract) TradePropose(ctx contractapi.TransactionContextInterface, medicineId string, receiver string) error {
	medicine, err := s.ReadMedicine(ctx, medicineId)
	if err != nil {
		return err
	}
	medicine.Receiver = receiver
	medicine.TractionType = "待接收"
	medicineJSON, err := json.Marshal(medicine)
	if err != nil {
		return err
	}
	return ctx.GetStub().PutState(medicineId, medicineJSON)
}

func (s *SmartContract) TradeReceive(ctx contractapi.TransactionContextInterface, medicineId string) error {
	medicine, err := s.ReadMedicine(ctx, medicineId)
	if err != nil {
		return err
	}
	medicine.Owner = medicine.Receiver
	medicine.Receiver = ""
	medicine.TractionType = "可交易"
	medicineJSON, err := json.Marshal(medicine)
	if err != nil {
		return err
	}
	return ctx.GetStub().PutState(medicineId, medicineJSON)
}

// DeleteMedicine 删除
func (s *SmartContract) Delete(ctx contractapi.TransactionContextInterface, id string) error {
	exists, err := s.MedicineExists(ctx, id)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the medicine %s does not exist", id)
	}

	return ctx.GetStub().DelState(id)
}

// MedicineExists 存在判断
func (s *SmartContract) MedicineExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
	medicineJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return false, fmt.Errorf("failed to read from world state: %v", err)
	}

	return medicineJSON != nil, nil
}

func (sc *SmartContract) Update(ctx contractapi.TransactionContextInterface, key string, value string) {
	existing, err := ctx.GetStub().GetState(key)

	if err != nil {
		fmt.Print("Unable to interact with world state")
		return
	}

	if existing == nil {
		fmt.Printf("Cannot update world state pair with key %s. Does not exist", key)
		return
	}

	err = ctx.GetStub().PutState(key, []byte(value))

	if err != nil {
		fmt.Print("Unable to interact with world state")
	}
}

func (sc *SmartContract) Create(ctx contractapi.TransactionContextInterface, key string, value string) error {
	existing, err := ctx.GetStub().GetState(key)

	if err != nil {
		fmt.Println("error")
		return errors.New("Unable to interact with world state")
	}

	if existing != nil {
		fmt.Println("error")
		return fmt.Errorf("Cannot create world state pair with key %s. Already exists", key)
	}

	err = ctx.GetStub().PutState(key, []byte(value))

	if err != nil {
		fmt.Println("error")
		return errors.New("Unable to interact with world state")
	}

	return nil
}
